//
// Created by 67678 on 2023/3/14.
//

#include "timer.h"
#include <functional>
#include <condition_variable>
#include <chrono>
#include <optional>
#include <thread>
#include <mutex>
#include <future>
#include "../event/eventLoop.h"
#include "../event/event.h"
#include "log.h"

using namespace std;
using namespace chrono;
using namespace event;

namespace timer {

Timer::Timer() {
    log("timer start running!");
}

Timer::~Timer() {
    log("timer end running!");
    close();
}

void Timer::loop() {
    log("timer looping!");
    mutex mx;
    unique_lock ul(mx);
    EventLoop &eventLoop = EventLoop::getInstance();

    while (!done_) {
        cv_.wait_until(ul, next_expire_time_);
        lock_guard lg(mx_);
        while (!times_.empty() && times_.begin()->expire_time <= high_resolution_clock::now()) {
            Time tm = *(times_.begin());
            times_.erase(times_.begin());
            if (tm.times > 1 || tm.times == -1) {
                Time tmp = tm;
                if (tmp.times > 1) --tmp.times;
                tmp.expire_time += tmp.loop_time;
                auto res = times_.insert(tmp);
            }
            if (times_.empty()) next_expire_time_ = time_point<high_resolution_clock>::max();
            else next_expire_time_ = times_.begin()->expire_time;
            eventLoop.addEvent(RunSelfEvent::make_event(bind(tm.callback, tm)));
        }

    }
}

void Timer::init() {
    log("timer initiating!");
    thread t(&Timer::loop, this);
    t.detach();
}

int Timer::addTimer_for(chrono::microseconds loop_time, function<void(Time)> callback, int times) {
    Time tm;
    tm.expire_time = high_resolution_clock::now() + loop_time;
    tm.loop_time = loop_time;
    tm.tfd = ++counter_;
    tm.times = times;
    tm.callback = callback;
    if (!addTime(tm)) throw runtime_error("timer add failed");
    return tm.tfd;
}


int Timer::addTimer_until(chrono::time_point<chrono::high_resolution_clock> end_time, function<void(Time)> callback) {
    return (addTimer_for(duration_cast<microseconds>(end_time - high_resolution_clock::now()), std::move(callback), 1));
}

bool Timer::delTimer(int tfd) {
    return delTime(tfd);
}

bool Timer::addTime(const Time &tm) {
    lock_guard lg(mx_);
    if (times_.contains(tm)) return false;
    times_.insert(tm);
    if (tm.expire_time < next_expire_time_) {
        next_expire_time_ = tm.expire_time;
        cv_.notify_one();
    }
    return true;
}

bool Timer::delTime(int tfd) {
    lock_guard lg(mx_);
    for (const Time &tm: times_) {
        if (tm.tfd == tfd) {
            times_.erase(tm);
            return true;
        }
    }
    return false;
}

void Timer::close() {
    log("timer closing!");

    if (done_)return;
    done_ = true;
}


}